package easyScript.start;

import basicutil.common.StringUtil;
import basicutil.dynamic.CompileClassFileInfo;
import basicutil.dynamic.CompileClassFileInfoDAO;
import basicutil.dynamic.DynamicCompileUtil;
import basicutil.dynamic.JdkDynamicCompileClassLoader;
import com.alibaba.fastjson2.util.BeanUtils;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.Separator;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import lombok.Getter;
import lombok.Setter;

import java.io.File;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class PluginController {
    CompileClassFileInfoDAO ccfiDAO = CompileClassFileInfoDAO.INSTANCE;
    public static final PluginController INSTANCE = new PluginController();
    public ObjectProperty<Node> centerNode = new SimpleObjectProperty<>();

    Map<String, Node> compiledNode = new HashMap<>();

    private PluginController() {
    }

    // 插件管理界面
    public void switch2pluginsView() {

        GridPane gridPane = new GridPane();
        gridPane.setHgap(10);
        gridPane.setVgap(10);
        DynamicObject dynamicObject = new DynamicObject(ccfiDAO.queryByName("测试打开窗口"));
        DynamicObject dynamicObject2 = new DynamicObject(ccfiDAO.queryByName("附加到主窗口"));
        gridPane.add(dynamicObject2.view4Builtin(), 0, 0);
        gridPane.add(dynamicObject.view4AutoOpen(), 1, 0);
        BorderPane.setMargin(gridPane, new Insets(10, 0, 0, 10));
        centerNode.set(gridPane);
    }

    public void switch2BuiltinView() {
        Button backBtn = new Button("返回插件窗口");
        backBtn.setOnAction(e -> switch2pluginsView());
        HBox hb = new HBox(new Label("Node型插件"), backBtn);
        hb.setSpacing(20);
        hb.setAlignment(Pos.BOTTOM_LEFT);
        VBox vBox = new VBox(hb);


        CompileClassFileInfo ccfi = ccfiDAO.queryByName("附加到主窗口");
        Node node = builtinNodeView(ccfi);


        BorderPane.setMargin(vBox, new Insets(10, 0, 0, 10));

        vBox.getChildren().addAll(node);
        vBox.setSpacing(10);
        centerNode.set(vBox);
    }

    private Node builtinNodeView(CompileClassFileInfo ccfi) {
        ListView<Node> listView = new ListView<>();
        BorderPane borderPane = new BorderPane();

        ObjectProperty<Node> builtinNode = new SimpleObjectProperty<>();
        builtinNode.unbindBidirectional(borderPane.centerProperty());
        borderPane.centerProperty().bindBidirectional(builtinNode);
        HBox ret = new HBox(listView, borderPane);

        String dirPath = ccfi.getFile4Compile();
        File file = Paths.get(dirPath).toFile();
        if (!file.isDirectory()) {
            return ret;
        }

        listView.getSelectionModel().selectedItemProperty().addListener(e -> {
            HBox selectedItem = (HBox) (listView.getSelectionModel().getSelectedItem());
            Label label = (Label) selectedItem.getChildren().get(1);
            Node value = compiledNode.get(label.getText());
            builtinNode.set(value);
        });
        File[] files = file.listFiles();
        for (File f : files) {
            try {
                CheckBox checkBox = new CheckBox();
                String name = f.getName();
                HBox box = new HBox(checkBox, new Label(name));
                listView.getItems().add(box);
                checkBox.setSelected(compiledNode.containsKey(name));
                checkBox.selectedProperty().addListener(e -> {
                    if (checkBox.isSelected()) {
                        if (!compiledNode.containsKey(name)) {
                            try {
                                JdkDynamicCompileClassLoader classLoader = DynamicCompileUtil.compile(new String[]{f.getAbsolutePath()}, ccfi.getClassPath());
                                Class<?> aClass = classLoader.loadClass(classLoader.getMainClassName());
                                Node o = (Node) aClass.newInstance();
                                compiledNode.put(name, o);
                            } catch (Exception ex) {
                                ex.printStackTrace();
                            }
                        }

                    } else {
                        compiledNode.remove(name);

                    }
                    listView.getSelectionModel().select(box);
                });


            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        return ret;

    }

    @Setter
    @Getter
    class DynamicObject {
        private CompileClassFileInfo ccfi;
        private Class<?> outClass;
        private Object outObj;

        public DynamicObject(CompileClassFileInfo ccfi) {
            this.ccfi = ccfi;
        }

        public void compile() throws Exception {
            if (outObj != null) {
                try {
                    close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            JdkDynamicCompileClassLoader classLoader = DynamicCompileUtil.compile(new String[]{ccfi.getFile4Compile()}, ccfi.getClassPath());
            String cm = StringUtil.getOrDefault(ccfi.getClassFullName(), classLoader.getMainClassName());
            outClass = classLoader.loadClass(cm);
            outObj = outClass.newInstance();
        }

        public void start() throws Exception {
            if (outObj == null) {
                return;
            }
            String m = ccfi.getOnStartMethod();
            if (StringUtil.isNotEmpty(m)) {
                outClass.getMethod(m).invoke(outObj);
            }
        }

        public void close() throws Exception {
            if (outObj == null) {
                return;
            }
            String m = ccfi.getOnCloseMethod();
            if (StringUtil.isNotEmpty(m)) {
                outClass.getMethod(m).invoke(outObj);
            }
        }

        VBox view4Builtin() {
            VBox vBox = new VBox();
            field2Vbox(vBox);
            vBox.setSpacing(5);


            vBox.getChildren().addAll(new Separator());
            VBox.setMargin(vBox, new Insets(10));
            VBox ret = new VBox(vBox);
            ret.setOnMouseClicked(e -> {
                // 切换root
                switch2BuiltinView();
            });
            ret.setStyle("-fx-background-color: rgba(200,200,200);");

            return ret;
        }

        VBox view4AutoOpen() {
            VBox vBox = new VBox();
            field2Vbox(vBox);

            Button recompileBtn = new Button("重新编译");
            recompileBtn.setOnAction(e -> {
                try {
                    compile();
                    start();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            });
            Button closeBtn = new Button("关闭");
            closeBtn.setOnAction(e -> {
                try {
                    close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            });

            vBox.getChildren().addAll(new Separator(), new HBox(recompileBtn, closeBtn));
            vBox.setSpacing(5);
            VBox.setMargin(vBox, new Insets(10));
            VBox ret = new VBox(vBox);
            ret.setStyle("-fx-background-color: rgba(200,200,200);");
            return ret;
        }

        private void field2Vbox(VBox vBox) {
            BeanUtils.declaredFields(CompileClassFileInfo.class, f -> {
                String fieldName = f.getName();
                TextField classFullName = new TextField();
                try {
                    f.setAccessible(true);
                    Object o = f.get(ccfi);
                    if (o == null) {
                        return;
                    }
                    classFullName.setText((String) o);
                    classFullName.textProperty().addListener((observable, oldValue, newValue) -> {
                        try {
                            f.set(ccfi, newValue);
                        } catch (IllegalAccessException e) {
                            classFullName.setText("error：" + e.getMessage());
                            e.printStackTrace();
                        }
                    });
                } catch (Exception e) {
                    classFullName.setText("error：" + e.getMessage());
                    e.printStackTrace();
                }
                vBox.getChildren().add(new VBox(new Label(fieldName), classFullName));
            });
        }
    }
}
